﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DevExpress.XtraEditors.Registrator;
using DevExpress.XtraEditors.Drawing;
using DevExpress.Accessibility;
using System.ComponentModel;
using DevExpress.XtraEditors.Mask;
using DevExpress.XtraEditors;
using DevExpress.Data.Mask;
using System.Windows.Forms;
using System.Drawing.Design;
using ProgNetComponentsDx.Controls.Design.Editors;
using ProgNetComponentsDx.Controls.Design.TypeConverters;

namespace ProgNetComponentsDx.Controls.Editors
{
    [ToolboxItem(true)]
    public class TimeDurationEdit : DevExpress.XtraEditors.BaseSpinEdit
    {
        public TimeDurationEdit()
            : base()
        {
            Width = 120;
        }
        protected override void CreateRepositoryItem()
        {
            fProperties = new RepositoryItemTimeDurationEdit(this);
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
        Category("Properties")]
        public new RepositoryItemTimeDurationEdit Properties
        {
            get { return fProperties as RepositoryItemTimeDurationEdit; }
        }

        [TypeConverter(typeof(TimeDurationEditTypeConverter)),
        Editor(typeof(TimeDurationEditor), typeof(UITypeEditor))]
        public override object EditValue
        {
            get
            {
                return base.EditValue;
            }
            set
            {
                if (value is DateTime)
                {
                    DateTime d = (DateTime)value;
                    double db = GetSeconds(d);
                    if (db >= 0 && db <= Properties.MaxValue)
                    {
                        base.EditValue = db;
                        if (MaskManager != null && !MaskManager.GetCurrentEditValue().IsTheSame(EditValue))
                            MaskManager.SetInitialEditValue(EditValue);
                    }
                }
                else
                {
                    if (value.IsNull())
                    {
                        base.EditValue = value;
                        if (MaskManager != null && !MaskManager.GetCurrentEditValue().IsTheSame(EditValue))
                            MaskManager.SetInitialEditValue(EditValue);
                    }
                    else
                    {
                        double db = value.AsDouble(-1);
                        if (db >= 0 && db <= Properties.MaxValue)
                        {
                            base.EditValue = db;
                            if (MaskManager != null && !MaskManager.GetCurrentEditValue().IsTheSame(EditValue))
                                MaskManager.SetInitialEditValue(EditValue);
                        }
                    }
                }
            }
        }

        public override string Text
        {
            get
            {
                if (MaskManager != null)
                    return MaskManager.DisplayText;
                return base.Text;
            }
            set
            {
                base.Text = value;
            }
        }
        internal double GetSeconds(DateTime d)
        {
            int i = 0;
            switch (Properties.TimeMask)
            {
                case TimeMask.DayHourMinuteSecond:
                    i += d.Second + 60 * d.Minute + (60 * 60) * d.Hour + (60 * 60 * 24) * d.Day;
                    break;
                case TimeMask.DayHourMinute:
                    i += 60 * d.Minute + (60 * 60) * d.Hour + (60 * 60 * 24) * d.Day;
                    break;
                case TimeMask.HourMinuteSecond:
                    i += d.Second + 60 * d.Minute + (60 * 60) * d.Hour;
                    break;
                case TimeMask.HourMinute:
                    i += 60 * d.Minute + (60 * 60) * d.Hour;
                    break;
                case TimeMask.MinuteSecond:
                    i += d.Second + 60 * d.Minute;
                    break;
                default:
                    break;
            }
            return i;
        }

        internal TimeMaskManager MaskManager
        {
            get;
            set;
        }

        protected override DevExpress.Data.Mask.MaskManager CreateMaskManager(MaskProperties mask)
        {
            MaskManager = new TimeMaskManager(Properties);
            MaskManager.SetInitialEditValue(EditValue);
            return MaskManager;
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete && (Properties.IsNullInput || e.Control) && EditValue.IsNotNull() && (EditValue.AsDouble() == 0 || SelectionLength == Text.Length))
            {
                EditValue = null;
                e.Handled = true;
            }
            base.OnKeyDown(e);
        }
    }

    [UserRepositoryItem("Register")]
    public class RepositoryItemTimeDurationEdit : DevExpress.XtraEditors.Repository.RepositoryItemBaseSpinEdit
    {
        static RepositoryItemTimeDurationEdit()
        {
            Register();
        }
        public static void Register()
        {
            if (!EditorRegistrationInfo.Default.Editors.Contains("TimeDurationEdit"))
                EditorRegistrationInfo.Default.Editors.Add(
                    new EditorClassInfo("TimeDurationEdit",
                        typeof(TimeDurationEdit),
                        typeof(RepositoryItemTimeDurationEdit), 
                        typeof(DevExpress.XtraEditors.ViewInfo.BaseSpinEditViewInfo), 
                        new ButtonEditPainter(), true, null, 
                        typeof(PopupEditAccessible)));
        }
        public const double MAX_VAL = ((double)(24 * 60 * 60) * (double)(1000000));

        private TimeMask _TimeMask = Editors.TimeMask.HourMinuteSecond;
        private bool _DisplayTimeChars = false;
        private string _DayString = "D";
        private string _HourString = "H";
        private string _MinuteString = "M";
        private string _SecondsString = "S";
        private double _MaxValue = MAX_VAL;

        public override string EditorTypeName
        {
            get
            {
                return "TimeDurationEdit";
            }
        }
        public RepositoryItemTimeDurationEdit()
        {
            Init();
        }

        private void Init()
        {
            AllowNullInput = DevExpress.Utils.DefaultBoolean.True;
            this.FormatEditValue += new DevExpress.XtraEditors.Controls.ConvertEditValueEventHandler(RepositoryItemTimeDurationEdit_FormatEditValue);
        }

        protected override MaskProperties CreateMaskProperties()
        {
            return new TimeMaskProperties();
        }

        public override void Assign(DevExpress.XtraEditors.Repository.RepositoryItem item)
        {
            base.Assign(item);
            RepositoryItemTimeDurationEdit source = item as RepositoryItemTimeDurationEdit;
            if (source != null)
            {
                this.BeginUpdate();
                this._DayString = source.DayString;
                this._DisplayTimeChars = source._DisplayTimeChars;
                this._HourString = source._HourString;
                this._MinuteString = source._MinuteString;
                this._SecondsString = source._SecondsString;
                this._TimeMask = source._TimeMask;
                this._MaxValue = source._MaxValue;
                SetMask();
                this.EndUpdate();
            }
            if (this.IsLockUpdate)
                OnPropertiesChanged();
        }
       
        public override string GetDisplayText(DevExpress.Utils.FormatInfo format, object editValue)
        {
            return TimeMaskManager.GetString(this, editValue);
        }
        public override string GetDisplayText(object editValue)
        {
            return TimeMaskManager.GetString(this, editValue);
        }
        
        internal bool IsNullInput
        {
            get { return IsNullInputAllowed; }
        }
        
        [Browsable(false)]
        [EditorBrowsable( EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        RefreshProperties(System.ComponentModel.RefreshProperties.All),
        NotifyParentProperty(true)]
        public override DevExpress.XtraEditors.Mask.MaskProperties Mask
        {
            get
            {
                return base.Mask;
            }
        }

        [Browsable(false)]
        public new TimeDurationEdit OwnerEdit
        {
            get { return base.OwnerEdit as TimeDurationEdit; }
        }

        #region Additional Properties
        [Category(ProgNetComponents.Constants.Categories.ProgNetTimeSettings),
        DefaultValue(TimeMask.HourMinuteSecond),
        RefreshProperties(System.ComponentModel.RefreshProperties.All),
        NotifyParentProperty(true)]
        public TimeMask TimeMask
        {
            get { return _TimeMask; }
            set
            {
                _TimeMask = value;
                SetMask();
            }
        }

         [Category(ProgNetComponents.Constants.Categories.ProgNetTimeSettings),
        DefaultValue(false),
        RefreshProperties(System.ComponentModel.RefreshProperties.All),
        NotifyParentProperty(true)]
        public bool DisplayTimeChars
        {
            get { return _DisplayTimeChars; }
            set
            {
                _DisplayTimeChars = value;
                SetMask();
            }
        }

         [Category(ProgNetComponents.Constants.Categories.ProgNetTimeSettings),
        DefaultValue("D"),
        RefreshProperties(System.ComponentModel.RefreshProperties.All),
        NotifyParentProperty(true)]
        public string DayString
        {
            get { return _DayString; }
            set { _DayString = value; SetMask(); }
        }
        
        [Category(ProgNetComponents.Constants.Categories.ProgNetTimeSettings),
        DefaultValue("H"),
        RefreshProperties(System.ComponentModel.RefreshProperties.All),
        NotifyParentProperty(true)]
        public string HourString
        {
            get { return _HourString; }
            set { _HourString = value; SetMask(); }
        }
        
        [Category(ProgNetComponents.Constants.Categories.ProgNetTimeSettings),
        DefaultValue("M"),
        RefreshProperties(System.ComponentModel.RefreshProperties.All),
        NotifyParentProperty(true)]
        public string MinuteString
        {
            get { return _MinuteString; }
            set { _MinuteString = value; SetMask(); }
        }
       
        [Category(ProgNetComponents.Constants.Categories.ProgNetTimeSettings),
        DefaultValue("S"),
        RefreshProperties(System.ComponentModel.RefreshProperties.All),
        NotifyParentProperty(true)]
        public string SecondsString
        {
            get { return _SecondsString; }
            set { _SecondsString = value; SetMask(); }
        }
       
        [Category(ProgNetComponents.Constants.Categories.ProgNetTimeSettings),
        DefaultValue(MAX_VAL),
        Editor(typeof(TimeDurationEditor), typeof(UITypeEditor)),
        TypeConverter(typeof(TimeDurationEditTypeConverter)),
        RefreshProperties(System.ComponentModel.RefreshProperties.All),
        NotifyParentProperty(true)]
        public double MaxValue
        {
            get { return _MaxValue; }
            set
            {
                if (value <= MAX_VAL && value > 0)
                {
                    _MaxValue = value;
                    SetMask();
                }
            }
        }
        #endregion

        private bool isChangingMask = false;
        private void SetMask()
        {
            if (isChangingMask)
                return;
            isChangingMask = true;
            (Mask as TimeMaskProperties).AllowSet = true;
            Mask.EditMask = "";
            Mask.MaskType = MaskType.None;
            switch (TimeMask)
            {
                case TimeMask.DayHourMinuteSecond:
                    if(DisplayTimeChars)
                    Mask.EditMask = "D H:M:S";
                    break;
                case TimeMask.DayHourMinute:
                    Mask.EditMask = "D H:M";
                    break;
                case TimeMask.HourMinuteSecond:
                    Mask.EditMask = "H:M:S";
                    break;
                case TimeMask.HourMinute:
                    Mask.EditMask = "H:M";
                    break;
                case TimeMask.MinuteSecond:
                    Mask.EditMask = "M:S";
                    break;
                default:
                    break;
            }
            Mask.MaskType = MaskType.Custom;
            (Mask as TimeMaskProperties).AllowSet = false;
            TimeMask m = TimeMask;
            TimeMask = Editors.TimeMask.DayHourMinute;
            TimeMask = Editors.TimeMask.DayHourMinuteSecond;
            TimeMask = m;
            isChangingMask = false;
            if (this.IsLockUpdate && OwnerEdit != null && OwnerEdit.MaskManager != null)
            {
                OwnerEdit.MaskManager.Prepare(this);
                this.RunMethod("RaisePropertiesChanged", EventArgs.Empty);
            }
        }

        void RepositoryItemTimeDurationEdit_FormatEditValue(object sender, DevExpress.XtraEditors.Controls.ConvertEditValueEventArgs e)
        {
            e.Value = TimeMaskManager.GetString(this, e.Value);
            e.Handled = true;
        }

        public RepositoryItemTimeDurationEdit(TimeDurationEdit edit)
        {
            Init();
            SetOwnerEdit(edit);
        }
    }

    public class TimeMaskProperties : MaskProperties
    {
        internal bool AllowSet
        {
            get;
            set;
        }
        public TimeMaskProperties()
        {
            fMaskType = DevExpress.XtraEditors.Mask.MaskType.RegEx;
        }
        public TimeMaskProperties(TimeMaskProperties data):base(data)
        {
            fMaskType = DevExpress.XtraEditors.Mask.MaskType.RegEx;
        }
       
        [NotifyParentProperty(true),
        RefreshProperties(System.ComponentModel.RefreshProperties.All)]
        public override string EditMask
        {
            get
            {
                return base.EditMask;
            }
            set
            {
                if (AllowSet)
                {
                    base.EditMask = value;
                }
            }
        }
       
        internal void OnMaskChange()
        {
            this.RaiseAfterChange();
        }
    }

    public class TimeMaskManager : DevExpress.Data.Mask.MaskManager
    {
        private static Dictionary<TimeMask, string> _Masks = null;
        public static Dictionary<TimeMask, string> Masks
        {
            get
            {
                if (_Masks == null)
                {
                    _Masks = new Dictionary<TimeMask, string>();
                    _Masks.Add(TimeMask.DayHourMinuteSecond, "(\\d+) (\\d+):(\\d+):(\\d+)");
                    _Masks.Add(TimeMask.DayHourMinute, "(\\d+) (\\d+):(\\d+)");
                    _Masks.Add(TimeMask.HourMinuteSecond, "(\\d+):(\\d+):(\\d+)");
                    _Masks.Add(TimeMask.HourMinute, "(\\d+):(\\d+)");
                    _Masks.Add(TimeMask.MinuteSecond, "(\\d+):(\\d+)");
                }
                return _Masks;
            }
        }

        #region Private types
        enum TimeObjectType
        {
            None,
            Day,
            Hour,
            Minute,
            Second
        }

        private class TimeObject
        {
            public TimeObject(TimeObjectType type, double maxValue, int minLength, double value = 0)
            {
                Type = type;
                MaxValue = maxValue;
                MinLength = minLength;
                Value = value;
                switch (type)
                {
                    case TimeObjectType.Minute:
                        Interval = 60;
                        break;
                    case TimeObjectType.Hour:
                        Interval = 60 * 60;
                        break;
                    case TimeObjectType.Day:
                        Interval = 24 * 60 * 60;
                        break;
                    default:
                        Interval = 1;
                        break;
                }
            }
            public int Interval
            {
                get;
                private set;
            }
            public TimeObjectType Type
            {
                get;
                set;
            }
            public double MaxValue
            {
                get;
                set;
            }
            public int MinLength
            {
                get;
                set;
            }
            public double Value
            {
                get;
                set;
            }
            public override string ToString()
            {
                string str = Value.ToString("N0");
                while (str.Length < MinLength)
                    str = "0" + str;
                return str;
            }
            public int Length
            {
                get { return ToString().Length; }
            }
        }

        private class SeparatorTimeObject : TimeObject
        {
            public SeparatorTimeObject(string separator)
                : base(TimeObjectType.None, 0, 1, 0)
            {
                Separator = separator;
            }

            public string Separator
            {
                get;
                set;
            }
            public override string ToString()
            {
                return Separator;
            }
        }
        #endregion

        #region fields
        
        private RepositoryItemTimeDurationEdit repository;
        private List<TimeObject> allObjects = new List<TimeObject>();
        private Timer insertTimer;

        private TimeObject dObject;
        private TimeObject hObject;
        private TimeObject mObject;
        private TimeObject sObject;

        private TimeObject selectedObject;
        private double? _CurrentValue;
        private double? PrevValue;

        private int CursorPos = 0;
        private int SelectionLength = 0;
        private bool isInsering = false;

        #endregion

        #region Properties
        public TimeMask TimeMask
        {
            get;
            private set;
        }
        private double? CurrentValue
        {
            get { return _CurrentValue; }
            set
            {
                if (value.HasValue && value.Value < 0)
                    return;
                PrevValue = _CurrentValue;
                _CurrentValue = value;
                if (value.HasValue && repository!=null && value.Value > repository.MaxValue)
                    _CurrentValue = repository.MaxValue;
                if (value.HasValue && repository == null && value.Value > RepositoryItemTimeDurationEdit.MAX_VAL)
                    _CurrentValue = RepositoryItemTimeDurationEdit.MAX_VAL;

                double d = 0;
                double h = 0;
                double m = 0;
                double s = 0;
                if (_CurrentValue.HasValue)
                {
                    double val = value.Value;
                    switch (TimeMask)
                    {
                        case TimeMask.DayHourMinuteSecond:
                            d = val / (60 * 60 * 24);
                            val = val % (60 * 60 * 24);
                            h = val / (60 * 60);
                            val = val % (60 * 60);
                            m = val / 60;
                            s = val % 60;
                            break;
                        case TimeMask.DayHourMinute:
                            d = val / (60 * 60 * 24);
                            val = val % (60 * 60 * 24);
                            h = val / (60 * 60);
                            val = val % (60 * 60);
                            m = val / 60;
                            break;
                        case TimeMask.HourMinuteSecond:
                            h = val / (60 * 60);
                            val = val % (60 * 60);
                            m = val / 60;
                            s = val % 60;
                            break;
                        case TimeMask.HourMinute:
                            h = val / (60 * 60);
                            val = val % (60 * 60);
                            m = val / 60;
                            break;
                        case TimeMask.MinuteSecond:
                            m = val / 60;
                            s = val % 60;
                            break;
                        default:
                            break;
                    }
                }
                if (dObject != null)
                    dObject.Value = Math.Floor(d);
                if (hObject != null)
                    hObject.Value = Math.Floor(h);
                if (mObject != null)
                    mObject.Value = Math.Floor(m);
                if (sObject != null)
                    sObject.Value = Math.Floor(s);
                RaiseEditTextChanged();
            }
        }
        private bool DisplayTimeChars
        {
            get;
            set;
        }

        #endregion

        public TimeMaskManager(RepositoryItemTimeDurationEdit rep)
            : base()
        {
            if (rep != null)
                Prepare(rep);
            else
                this.repository = rep;
        }


        public void Prepare(RepositoryItemTimeDurationEdit repository)
        {
            allObjects.Clear();
            this.repository = repository;
            TimeMask = repository.TimeMask;
            insertTimer = new Timer();
            insertTimer.Interval = 500;
            insertTimer.Tick += new EventHandler(insertTimer_Tick);
            double maxVal = -1;
            double? cVal = CurrentValue;

            if (TimeMask == Editors.TimeMask.DayHourMinute || TimeMask == Editors.TimeMask.DayHourMinuteSecond)
            {
                dObject = new TimeObject(TimeObjectType.Day, -1, 1, 0);
                allObjects.Add(dObject);
                if (repository.DisplayTimeChars)
                    allObjects.Add(new SeparatorTimeObject(repository.DayString));
                allObjects.Add(new SeparatorTimeObject(" "));
                maxVal = 23;
            }
            if (TimeMask != Editors.TimeMask.MinuteSecond)
            {
                hObject = new TimeObject(TimeObjectType.Hour, maxVal, 1, 0);
                allObjects.Add(hObject);
                if (repository.DisplayTimeChars)
                {
                    allObjects.Add(new SeparatorTimeObject(repository.HourString));
                    allObjects.Add(new SeparatorTimeObject(" "));
                }
                else
                {
                    allObjects.Add(new SeparatorTimeObject(":"));
                }
                maxVal = 59;
                mObject = new TimeObject(TimeObjectType.Minute, maxVal, 2, 0);
                allObjects.Add(mObject);
                if (repository.DisplayTimeChars)
                    allObjects.Add(new SeparatorTimeObject(repository.MinuteString));
            }
            else
            {
                mObject = new TimeObject(TimeObjectType.Minute, -1, 1, 0);
                allObjects.Add(mObject);
                if (repository.DisplayTimeChars)
                    allObjects.Add(new SeparatorTimeObject(repository.MinuteString));
            }
            if (TimeMask != Editors.TimeMask.HourMinute && TimeMask != Editors.TimeMask.DayHourMinute)
            {
                if (repository.DisplayTimeChars)
                    allObjects.Add(new SeparatorTimeObject(" "));
                else
                    allObjects.Add(new SeparatorTimeObject(":"));
                sObject = new TimeObject(TimeObjectType.Second, 59, 2, 0);
                allObjects.Add(sObject);
                if (repository.DisplayTimeChars)
                    allObjects.Add(new SeparatorTimeObject(repository.SecondsString));
            }
            CurrentValue = cVal;
        }

        private void insertTimer_Tick(object sender, EventArgs e)
        {
            isInsering = false;
            insertTimer.Stop();
        }
        private void Select(TimeObject obj, bool forceSelection)
        {
            selectedObject = obj;
            CursorPos = 0;
            SelectionLength = 0;
            int idx = allObjects.IndexOf(obj);
            for (int i = 0; i < idx; i++)
            {
                CursorPos += allObjects[i].Length;
            }
            SelectionLength = obj.Length;
        }
        #region Override
        public override bool Backspace()
        {
            return Delete();
        }

        public override bool CanUndo
        {
            get { return PrevValue.HasValue; }
        }

        public override bool CursorEnd(bool forceSelection)
        {
            if (allObjects.Count > 0)
            {
                int idx = allObjects.Count - 1;
                while (idx > 0 && allObjects[idx] is SeparatorTimeObject)
                    idx--;
                Select(allObjects[idx], forceSelection);
                return true;
            }
            return false;
        }

        public override bool CursorHome(bool forceSelection)
        {
            if (allObjects.Count > 0)
            {
                int idx = 0;
                while (idx < (allObjects.Count - 1) && allObjects[idx] is SeparatorTimeObject)
                    idx++;
                Select(allObjects[idx], forceSelection);
                return true;
            }
            return false;
        }

        public override bool CursorLeft(bool forceSelection, bool isNeededKeyCheck)
        {
            if (!CurrentValue.HasValue)
                return false;
            
            int idx = allObjects.Count;
            if (selectedObject != null)
                idx = allObjects.IndexOf(selectedObject);
            
            if (idx == 0)
                return false;
            
            idx--;
            while (idx > -1 && allObjects[idx] is SeparatorTimeObject)
                idx--;

            if (idx > -1)
            {
                if (!isNeededKeyCheck)
                    Select(allObjects[idx], forceSelection);
                return true;
            }
            return false;
        }

        public override bool CursorRight(bool forceSelection, bool isNeededKeyCheck)
        {
            if (!CurrentValue.HasValue)
                return false;

            int idx = 0;
            if (selectedObject != null)
                idx = allObjects.IndexOf(selectedObject);
            
            if (idx == allObjects.Count - 1)
                return false;
            idx++;
            while (idx < allObjects.Count && allObjects[idx] is SeparatorTimeObject)
                idx++;
            if (idx < allObjects.Count)
            {
                if (!isNeededKeyCheck)
                    Select(allObjects[idx], forceSelection);
                return true;
            }
            return false;
        }

        public override bool CursorToDisplayPosition(int newPosition, bool forceSelection)
        {
            if (!CurrentValue.HasValue)
                return false;
            TimeObject s = null;
            int idx = 0;
            for (int i = 0; i < allObjects.Count; i++)
            {
                s = allObjects[i];
                idx += s.Length;
                if (idx >= newPosition)
                {
                    while (s is SeparatorTimeObject)
                    {
                        i--;
                        if (i > -1)
                        {
                            s = allObjects[i];
                        }
                        else
                        {
                            break;
                        }
                    }
                    if (!(s is SeparatorTimeObject))
                    {
                        Select(s, forceSelection);
                        return true;
                    }
                    return false;
                }
            }
            return false;
        }

        public override bool Delete()
        {
            if (selectedObject != null && selectedObject.Value != 0)
            {
                if (selectedObject.Type != TimeObjectType.None)
                {
                    CurrentValue -= selectedObject.Value * selectedObject.Interval;
                    return true;
                }
            }
            return false;
        }

        public override int DisplayCursorPosition
        {
            get { return CursorPos; }
        }

        public override int DisplaySelectionAnchor
        {
            get { return SelectionLength + CursorPos; }
        }

        public override string DisplayText
        {
            get
            {
                string text = "";
                if (CurrentValue.HasValue)
                {
                    foreach (TimeObject t in allObjects)
                    {
                        text += t.ToString();
                    }
                }
                return text;
            }
        }

        public override string GetCurrentEditText()
        {
            return DisplayText;
        }

        public override object GetCurrentEditValue()
        {
            if (CurrentValue.HasValue)
                return CurrentValue.Value;
            return null;
        }

        public override bool Insert(string insertion)
        {
            if (insertion == " ")
            {
                CursorRight(true);
                return true;
            }
            if (!CurrentValue.HasValue)
                CurrentValue = 0;
            if (selectedObject == null && allObjects.Count > 0)
                selectedObject = allObjects[0];
            if (selectedObject != null)
            {
                double val = insertion.AsDouble(-1);
                if (val > -1)
                {
                    double oldVal = selectedObject.Value;
                    if (isInsering)
                    {
                        double newVal = oldVal * 10 + val;
                        if (newVal <= selectedObject.MaxValue || selectedObject.MaxValue == -1)
                            CurrentValue += (newVal - oldVal) * selectedObject.Interval;
                        else
                            CurrentValue += (val - oldVal) * selectedObject.Interval;
                        insertTimer.Stop();
                        insertTimer.Start();
                    }
                    else
                    {
                        isInsering = true;
                        CurrentValue += (val - oldVal) * selectedObject.Interval;
                        insertTimer.Start();
                    }
                    Select(selectedObject, true);
                }
            }
            return true;
        }

        public override bool IsEditValueDifferFromEditText
        {
            get { return CurrentValue.HasValue; }
        }

        //public override bool IsPlainTextLike
        //{
        //    get { return true; }
        //}

        public override void SetInitialEditText(string initialEditText)
        {
            //throw new NotImplementedException();
        }

        public override void SetInitialEditValue(object initialEditValue)
        {
            if (initialEditValue.AsDouble(-1) != -1)
                CurrentValue = initialEditValue.AsDouble();
            else if (initialEditValue.IsNull())
                CurrentValue = null;
        }

        public override bool SpinDown()
        {
            if (!CurrentValue.HasValue)
                CurrentValue = 0;
            if (selectedObject == null && allObjects.Count > 0)
                selectedObject = allObjects[0];
            if (selectedObject != null && selectedObject.Type != TimeObjectType.None)
            {
                if (CurrentValue.Value >= selectedObject.Interval)
                {
                    CurrentValue -= selectedObject.Interval;
                    Select(selectedObject, true);
                    return true;
                }
                else
                {
                    int idx = allObjects.IndexOf(selectedObject);
                    idx++;
                    while (idx < allObjects.Count && allObjects[idx] is SeparatorTimeObject)
                        idx++;
                    if (idx < allObjects.Count)
                    {
                        Select(allObjects[idx], true);
                        return SpinDown();
                    }
                }
            }
            return false;
        }

        public override bool SpinUp()
        {
            if (!CurrentValue.HasValue)
                CurrentValue = 0;
            if (selectedObject == null && allObjects.Count > 0)
                selectedObject = allObjects[0];
            if (selectedObject != null && selectedObject.Type != TimeObjectType.None)
            {
                CurrentValue += selectedObject.Interval;
                Select(selectedObject, true);
                return true;
            }
            return false;
        }

        public override bool Undo()
        {
            if (PrevValue.HasValue)
            {
                CurrentValue = PrevValue;
                return true;
            }
            return false;
        }
        #endregion

        static TimeMaskManager _Instance = new TimeMaskManager(null);
        public static string GetString(RepositoryItemTimeDurationEdit repository, object value)
        {
            _Instance.Prepare(repository);
            _Instance.SetInitialEditValue(value);
            return _Instance.DisplayText;
        }
    }
    
    public enum TimeMask
    {
        DayHourMinuteSecond,
        DayHourMinute,
        HourMinuteSecond,
        HourMinute,
        MinuteSecond
    }
}
